package com.jfireframework.sql.parse.sqlSource;

import java.lang.reflect.Method;
import java.util.List;
import com.jfireframework.baseutil.StringUtil;
import com.jfireframework.baseutil.collection.StringCache;
import com.jfireframework.baseutil.smc.SmcHelper;
import com.jfireframework.sql.SqlSession;
import com.jfireframework.sql.annotation.Sql;
import com.jfireframework.sql.parse.lexer.Lexer;
import com.jfireframework.sql.parse.lexer.token.Expression;
import com.jfireframework.sql.parse.lexer.token.Token;
import com.jfireframework.sql.transfer.resultset.ResultsetTransferStore;

public class DynamicSqlSource extends AbstractSqlSource
{
	private final ResultsetTransferStore resultsetTransferStore;
	
	public DynamicSqlSource(ResultsetTransferStore resultsetTransferStore)
	{
		this.resultsetTransferStore = resultsetTransferStore;
	}
	
	interface BuildReturn
	{
		String run(String methodBody);
	}
	
	String parse(Lexer lexer, Method method, BuildReturn buildReturn)
	{
		String[] paramNames = method.getAnnotation(Sql.class).paramNames().split(",");
		Class<?>[] paramTypes = method.getParameterTypes();
		String methodBody = "";
		methodBody += SqlSession.class.getName() + " session = sessionFactory.getCurrentSession();\r\n";
		methodBody += "if(session==null){throw new java.lang.NullPointerException(\"current session 为空，请检查\");}\r\n";
		methodBody += "com.jfireframework.baseutil.collection.StringCache builder = new com.jfireframework.baseutil.collection.StringCache();\r\n";
		methodBody += "java.util.List list = new java.util.ArrayList();\r\n";
		StringCache sqlCache = new StringCache();
		for (Token token : lexer.getTokens())
		{
			if (token.getTokenType() == Expression.BRACE)
			{
				if (sqlCache.count() != 0)
				{
					methodBody += "builder.append(\"" + sqlCache.toString() + "\");\r\n";
					sqlCache.clear();
				}
				methodBody += "builder.append(" + buildParam(token.getLiterals().substring(1, token.getLiterals().length() - 1), paramNames, paramTypes) + ").append(' ');\r\n";
			}
			else if (token.getTokenType() == Expression.VARIABLE)
			{
				if (sqlCache.count() != 0)
				{
					methodBody += "builder.append(\"" + sqlCache.toString() + "\");\r\n";
					sqlCache.clear();
				}
				methodBody += "builder.append(\"? \");\r\n";
				methodBody += "list.add(" + buildParam(token.getLiterals().substring(1), paramNames, paramTypes) + ");\r\n";
			}
			else if (token.getTokenType() == Expression.VARIABLE_WITH_TIDLE)
			{
				if (sqlCache.count() != 0)
				{
					methodBody += "builder.append(\"" + sqlCache.toString() + "\");\r\n";
					sqlCache.clear();
				}
				String content = token.getLiterals().substring(2);
				Class<?> paramType = SmcHelper.getType(content, paramNames, paramTypes);
				if (paramType == String.class)
				{
					methodBody += "{\r\n\tString[] tmp = ((String)" + buildInvoke(content, paramNames, paramTypes) + ").split(\",\");\r\n";
				}
				else if (paramType.isArray())
				{
					methodBody += "{\r\n\t" + paramType.getComponentType().getName() + "[] tmp = " + buildInvoke(content, paramNames, paramTypes) + ";\r\n";
				}
				else if (List.class.isAssignableFrom(paramType))
				{
					methodBody += "{\r\n\tjava.util.List tmp = " + buildInvoke(content, paramNames, paramTypes) + ";\r\n";
				}
				else
				{
					throw new RuntimeException("in操作中存在不识别的类型");
				}
				if (List.class.isAssignableFrom(paramType))
				{
					methodBody += "\tint length = tmp.size();\r\n";
				}
				else
				{
					methodBody += "\tint length = tmp.length;\r\n";
				}
				methodBody += "\tbuilder.append('(');\r\n";
				methodBody += "\tfor(int i=0;i<length;i++){builder.append(\"?,\");}\r\n";
				methodBody += "\tif(builder.isCommaLast()){builder.deleteLast().append(\")\");}\r\n";
				if (List.class.isAssignableFrom(paramType))
				{
					methodBody += "\tfor(int i=0;i<length;i++){list.add(tmp.get(i));}\r\n";
				}
				else
				{
					methodBody += "\tfor(int i=0;i<length;i++){list.add(tmp[i]);}\r\n";
				}
				methodBody += "}\r\n";
			}
			else if (token.getTokenType() == Expression.CONSTANT)
			{
				if (sqlCache.count() != 0)
				{
					methodBody += "builder.append(\"" + sqlCache.toString() + "\");\r\n";
					sqlCache.clear();
				}
				methodBody += "builder.append(\"? \");\r\n";
				methodBody += "list.add(" + token.getLiterals().substring(1) + ");\r\n";
			}
			else if (token.getTokenType() == Expression.IF)
			{
				if (sqlCache.count() != 0)
				{
					methodBody += "builder.append(\"" + sqlCache.toString() + "\");\r\n";
					sqlCache.clear();
				}
				String literals = token.getLiterals();
				literals = literals.substring(4);
				literals = literals.substring(0, literals.length() - 2);
				methodBody += "if(" + createIf(literals, paramNames, paramTypes) + ")\r\n";
				methodBody += "{\r\n";
			}
			else if (token.getTokenType() == Expression.ENDIF)
			{
				methodBody += "}\r\n";
			}
			else
			{
				sqlCache.append(token.getLiterals()).append(" ");
			}
		}
		if (sqlCache.count() != 0)
		{
			methodBody += "builder.append(\"" + sqlCache.toString() + "\");\r\n";
			sqlCache.clear();
		}
		methodBody += "String sql = builder.toString();\r\n";
		methodBody = buildReturn.run(methodBody);
		return methodBody;
	}
	
	@Override
	public String parseSingleQuery(Lexer lexer, final Method method)
	{
		BuildReturn buildReturn = new BuildReturn() {
			
			@Override
			public String run(String methodBody)
			{
				int sn = resultsetTransferStore.registerTransfer(method);
				Class<?> returnType = method.getReturnType();
				if (returnType.isPrimitive())
				{
					if (returnType == int.class)
					{
						methodBody += "return (Integer)session.query(sessionFactory.getResultSetTransferStore().get("//
						        + sn + "),sql,list.toArray());\r\n";
					}
					else if (returnType == boolean.class)
					{
						methodBody += "return (Boolean)session.query(sessionFactory.getResultSetTransferStore().get("//
						        + sn + "),sql,list.toArray());\r\n";
					}
					else if (returnType == short.class)
					{
						methodBody += "return (Short)session.query(sessionFactory.getResultSetTransferStore().get("//
								+ sn + "),sql,list.toArray());\r\n";
					}
					else if (returnType == long.class)
					{
						methodBody += "return (Long)session.query(sessionFactory.getResultSetTransferStore().get("//
								+ sn + "),sql,list.toArray());\r\n";
					}
					else if (returnType == float.class)
					{
						methodBody += "return (Float)session.query(sessionFactory.getResultSetTransferStore().get("//
								+ sn + "),sql,list.toArray());\r\n";
					}
					else if (returnType == double.class)
					{
						methodBody += "return (Double)session.query(sessionFactory.getResultSetTransferStore().get("//
								+ sn + "),sql,list.toArray());\r\n";
					}
				}
				else
				{
					methodBody += "return (" + SmcHelper.getTypeName(method.getReturnType()) + ")session.query(sessionFactory.getResultSetTransferStore().get("//
					        + sn + "),sql,list.toArray());\r\n";
				}
				return methodBody;
			}
		};
		return parse(lexer, method, buildReturn);
	}
	
	@Override
	public String parseListQuery(Lexer lexer, final Method method)
	{
		BuildReturn buildReturn = new BuildReturn() {
			
			@Override
			public String run(String methodBody)
			{
				int sn = resultsetTransferStore.registerTransfer(method);
				methodBody += "return session.queryList(sessionFactory.getResultSetTransferStore().get(" + sn + "),sql,list.toArray());\r\n";
				return methodBody;
			}
		};
		return parse(lexer, method, buildReturn);
	}
	
	@Override
	public String parsePageQuery(Lexer lexer, final Method method)
	{
		BuildReturn buildReturn = new BuildReturn() {
			
			@Override
			public String run(String methodBody)
			{
				String pageVariable = "$" + (method.getParameterTypes().length - 1);
				int sn = resultsetTransferStore.registerTransfer(method);
				methodBody += "return session.queryList(sessionFactory.getResultSetTransferStore().get(" + sn + "),sql," + pageVariable + ",list.toArray());\r\n";
				return methodBody;
			}
		};
		return parse(lexer, method, buildReturn);
	}
	
	@Override
	public String parseUpdate(Lexer lexer, final Method method)
	{
		BuildReturn buildReturn = new BuildReturn() {
			
			@Override
			public String run(String methodBody)
			{
				methodBody += "int updateRows=session.update(sql,list.toArray());\r\n";
				if (method.getReturnType() == Void.class || method.getReturnType() == void.class)
				{
					;
				}
				else
				{
					methodBody += "return updateRows;\r\n";
				}
				return methodBody;
			}
		};
		return parse(lexer, method, buildReturn);
	}
	
	public String createIf(String el, String[] paramNames, Class<?>[] types)
	{
		StringCache cache = new StringCache();
		int index = 0;
		int end = 0;
		while (index < el.length())
		{
			char c = el.charAt(index);
			if (c == '$')
			{
				end = getEndFlag(el, index);
				String content = el.substring(index + 1, end);
				cache.append(buildInvoke(content, paramNames, types));
				Class<?> type = SmcHelper.getType(content, paramNames, types);
				if (type == String.class)
				{
					index = end;
					while ((c = el.charAt(index)) == ' ')
					{
						index += 1;
					}
					if (c == '=' && el.charAt(index + 1) == '=')
					{
						index += 2;
						do
						{
							end = getEndFlag(el, index + 1);
							content = el.substring(index, end);
							index = end;
							content = content.trim();
						} while ("".equals(content));
						if (content.equals("null"))
						{
							cache.append("==null");
						}
						else
						{
							if (content.charAt(0) != '"' || content.endsWith("\"") == false)
							{
								throw new UnsupportedOperationException(StringUtil.format("解析条件语句存在问题，其if判断中存在对String的比较，但是却没有使用'\"'将字符串包围。请检查:{}", el));
							}
							cache.append(".equals(").append(content).append(")");
						}
					}
					else if (c == '!' && el.charAt(index + 1) == '=')
					{
						index += 2;
						do
						{
							end = getEndFlag(el, index + 1);
							content = el.substring(index, end);
							index = end;
							content = content.trim();
						} while ("".equals(content));
						if (content.equals("null"))
						{
							cache.append("!=null");
						}
						else
						{
							if (content.charAt(0) != '"' || content.endsWith("\"") == false)
							{
								throw new UnsupportedOperationException(StringUtil.format("解析条件语句存在问题，其if判断中存在对String的比较，但是却没有使用'\"'将字符串包围。请检查:{}", el));
							}
							cache.append(".equals(").append(content).append(")==false");
						}
						index = end;
					}
					else
					{
						throw new UnsupportedOperationException();
					}
				}
			}
			else if (c == ' ')
			{
				cache.append(' ');
				index += 1;
				continue;
			}
			else
			{
				end = getEndFlag(el, index + 1);
				String content = el.substring(index, end);
				cache.append(content);
			}
			index = end;
		}
		return cache.toString();
	}
	
	private int getEndFlag(String sql, int start)
	{
		while (start < sql.length())
		{
			char c = sql.charAt(start);
			if (c == '>' || c == '<' || c == '!' || c == '=' || c == ' ' || c == ',' //
			        || c == '#' || c == '+' || c == '-' || c == '(' || c == ')' || c == ']' || c == '[')
			{
				break;
			}
			start++;
		}
		return start;
	}
	
}
